What do the results look like?
dirViking <- c(
file.path(
getwd(), "LCAB_LawMorton1996-NumericalPoolCommunityScaling"
),
file.path(
getwd(), "LCAB_LawMorton1996-NumericalPoolCommunityScaling2"
)
)
dirVikingResults <- file.path(
dirViking, c("results-2021-04", "save-2021-05-10") # Latter not 100% yet.
)
resultFormat <- paste0(
"run-",
"%d", # Combination Number, or CombnNum.
"-",
"%s", # Run Seed.
".RDS"
)
2021-04 Data
# Copied from LawMorton1996-NumericalPoolCommunityScaling-Calculation.R
# TODO: In the future, make this a separate file that everyone can call...
set.seed(38427042)
basal <- c(3, 10, 30, 100, 300, 1000)
consumer <- c(3, 10, 30, 100, 300, 1000) * 2
events <- (max(basal) + max(consumer)) * 2
runs <- 100
logBodySize <- c(-2, -1, -1, 1) # Morton and Law 1997 version.
parameters <- c(0.01, 10, 0.5, 0.2, 100, 0.1)
# Need to rerun seedsPrep to get the random number generation right for seedsRun
seedsPrep <- runif(2 * length(basal) * length(consumer)) * 1E8
seedsRun <- runif(runs * length(basal) * length(consumer)) * 1E8
paramFrame <- with(list(
b = rep(basal, times = length(consumer)),
c = rep(consumer, each = length(basal)),
s1 = seedsPrep[1:(length(basal) * length(consumer))],
s2 = seedsPrep[
(length(basal) * length(consumer) + 1):(
2 * length(basal) * length(consumer))
],
sR = seedsRun
), {
temp <- data.frame(
CombnNum = 0,
Basals = b,
Consumers = c,
SeedPool = s1,
SeedMat = s2,
SeedRuns = "",
SeedRunsNum = 0,
EndStates = I(rep(list(""), length(b))),
EndStatesNum = 0,
EndStateSizes = I(rep(list(""), length(b))),
EndStateSizesNum = NA,
EndStateAssembly = I(rep(list(""), length(b))),
EndStateAbundance = I(rep(list(""), length(b))),
Dataset = "2021-04",
DatasetID = 1,
stringsAsFactors = FALSE
)
for (i in 1:nrow(temp)) {
seeds <- sR[((i - 1) * runs + 1) : (i * runs)]
temp$SeedRuns[i] <- toString(seeds) # CSV
temp$SeedRunsNum[i] <- length(seeds)
}
temp$CombnNum <- 1:nrow(temp)
temp
})
# Note: n + 2 end states. Failure to finish, failure to obtain state, and state.
for (i in 1:nrow(paramFrame)) {
resultsList <- list(
"No Run" = 0,
"No State" = 0
)
resultsSize <- list(
"0" = 0
)
resultsAssembly <- list(
"No Run" = data.frame(),
"No State" = data.frame()
)
seeds <- unlist(strsplit(paramFrame$SeedRuns[i], ', '))
for (seed in seeds) {
fileName <- file.path(
dirVikingResults[paramFrame$DatasetID[i]],
sprintf(resultFormat, paramFrame$CombnNum[i], seed)
)
if (file.exists(fileName)) {
temp <- load(fileName)
temp <- eval(parse(text = temp)) # Get objects.
if (is.data.frame(temp)) {
community <- toString(
temp[[ncol(temp)]][[nrow(temp)]]
)
size <- toString(length(temp[[ncol(temp)]][[nrow(temp)]]))
if (community == "") {
resultsList$`No State` <- resultsList$`No State` + 1
resultsSize$`0` <- resultsSize$`0` + 1
} else if (community %in% names(resultsList)) {
resultsList[[community]] <- resultsList[[community]] + 1
resultsSize[[size]] <- resultsSize[[size]] + 1
} else {
resultsList[[community]] <- 1
resultsAssembly[[community]] <- temp
if (size %in% resultsSize) {
resultsSize[[size]] <- resultsSize[[size]] + 1
} else {
resultsSize[[size]] <- 1
}
}
} else {
resultsList$`No State` <- resultsList$`No State` + 1
resultsSize$`0` <- resultsSize$`0` + 1
}
} else {
resultsList$`No Run` <- resultsList$`No Run` + 1
resultsSize$`0` <- resultsSize$`0` + 1
}
}
paramFrame$EndStates[[i]] <- resultsList
paramFrame$EndStatesNum[i] <- length(resultsList) - 2 # ! No State, No Run
paramFrame$EndStateSizes[[i]] <- resultsSize
paramFrame$EndStateSizesNum[i] <- length(resultsSize) - 1 # ! 0
paramFrame$EndStateAssembly[[i]] <- resultsAssembly
}
2021-05 Data
source(
file.path(getwd(),
"LawMorton1996-NumericalPoolCommunityScaling-Settings2.R")
)
oldNrow <- nrow(paramFrame)
paramFrame <- rbind(paramFrame, with(list(
b = rep(basal, times = length(consumer)),
c = rep(consumer, each = length(basal)),
s1 = seedsPrep[1:(length(basal) * length(consumer))],
s2 = seedsPrep[
(length(basal) * length(consumer) + 1):(
2 * length(basal) * length(consumer))
],
sR = seedsRun
), {
temp <- data.frame(
CombnNum = 0,
Basals = b,
Consumers = c,
SeedPool = s1,
SeedMat = s2,
SeedRuns = "",
SeedRunsNum = 0,
EndStates = I(rep(list(""), length(b))),
EndStatesNum = 0,
EndStateSizes = I(rep(list(""), length(b))),
EndStateSizesNum = NA,
EndStateAssembly = I(rep(list(""), length(b))),
EndStateAbundance = I(rep(list(""), length(b))),
Dataset = "2021-05",
DatasetID = 2,
stringsAsFactors = FALSE
)
for (i in 1:nrow(temp)) {
seeds <- sR[((i - 1) * runs + 1) : (i * runs)]
temp$SeedRuns[i] <- toString(seeds) # CSV
temp$SeedRunsNum[i] <- length(seeds)
}
temp$CombnNum <- 1:nrow(temp)
temp
})
)
# Note: n + 2 end states. Failure to finish, failure to obtain state, and state.
# Modified from above, but with the abundance recorded.
for (i in (oldNrow + 1):nrow(paramFrame)) {
resultsList <- list(
"No Run" = 0,
"No State" = 0
)
resultsSize <- list(
"0" = 0
)
resultsAssembly <- list(
"No Run" = data.frame(),
"No State" = data.frame()
)
resultsAbund <- list(
"No Run" = "",
"No State" = ""
)
seeds <- unlist(strsplit(paramFrame$SeedRuns[i], ', '))
for (seed in seeds) {
fileName <- file.path(
dirVikingResults[paramFrame$DatasetID[i]],
sprintf(resultFormat, paramFrame$CombnNum[i], seed)
)
if (file.exists(fileName)) {
temp <- load(fileName)
temp <- eval(parse(text = temp)) # Get objects.
if (is.list(temp) && "Result" %in% names(temp)) {
if (is.data.frame(temp$Result))
community <- temp$Result$Community[[nrow(temp$Result)]]
else
community <- temp$Result
size <- toString(length(community))
if (community[1] != "")
abund <- toString(temp$Abund[community + 1])
else
abund <- ""
community <- toString(community)
if (community == "") {
resultsList$`No State` <- resultsList$`No State` + 1
resultsSize$`0` <- resultsSize$`0` + 1
} else if (community %in% names(resultsList)) {
resultsList[[community]] <- resultsList[[community]] + 1
resultsSize[[size]] <- resultsSize[[size]] + 1
} else {
resultsList[[community]] <- 1
resultsAssembly[[community]] <- temp
resultsAbund[[community]] <- abund
if (size %in% resultsSize) {
resultsSize[[size]] <- resultsSize[[size]] + 1
} else {
resultsSize[[size]] <- 1
}
}
} else {
resultsList$`No State` <- resultsList$`No State` + 1
resultsSize$`0` <- resultsSize$`0` + 1
}
} else {
resultsList$`No Run` <- resultsList$`No Run` + 1
resultsSize$`0` <- resultsSize$`0` + 1
}
}
paramFrame$EndStates[[i]] <- resultsList
paramFrame$EndStatesNum[i] <- length(resultsList) - 2 # ! No State, No Run
paramFrame$EndStateSizes[[i]] <- resultsSize
paramFrame$EndStateSizesNum[i] <- length(resultsSize) - 1 # ! 0
paramFrame$EndStateAssembly[[i]] <- resultsAssembly
paramFrame$EndStateAbundance[[i]] <- resultsAbund
}
Plot
# X, Y, Basal and Consumer.
# Z = Sizes of the Endstates.
plotScalingData <- data.frame(
CombnNum = rep(paramFrame$CombnNum, paramFrame$EndStatesNum),
Basals = rep(paramFrame$Basals, paramFrame$EndStatesNum),
Consumers = rep(paramFrame$Consumers, paramFrame$EndStatesNum),
Dataset = rep(paramFrame$Dataset, paramFrame$EndStatesNum),
DatasetID = rep(paramFrame$DatasetID, paramFrame$EndStatesNum)
)
# Communities
comms <- unlist(lapply(paramFrame$EndStates, names))
freqs <- unlist(paramFrame$EndStates)
asmbl <- unlist(paramFrame$EndStateAssembly, recursive = FALSE)
asmbl <- asmbl[comms != "No Run" & comms != "No State"]
freqs <- freqs[comms != "No Run" & comms != "No State"]
comms <- comms[comms != "No Run" & comms != "No State"]
asmbl <- lapply(asmbl, function(d) {
if ("Result.Outcome" %in% names(d))
d %>% dplyr::filter(Result.Outcome != "Type 1 (Failure)" &
Result.Outcome != "Present")
else
d$Result %>% dplyr::filter(Outcome != "Type 1 (Failure)" &
Outcome != "Present")
})
plotScalingData$Communities <- comms
plotScalingData$CommunityFreq <- freqs
plotScalingData$CommunitySeq <- asmbl
# Community Size
temp <- unlist(lapply(strsplit(plotScalingData$Communities, ','), length))
plotScalingData$CommunitySize <- temp
# For usage by the reader.
plotScaling <- plotly::plot_ly(
plotScalingData,
x = ~Basals,
y = ~Consumers,
z = ~CommunitySize,
color = ~Dataset
)
plotScaling <- plotly::add_markers(plotScaling)
plotScaling <- plotly::layout(
plotScaling,
scene = list(
xaxis = list(type = "log"),
yaxis = list(type = "log")
)
)
plotScaling
minimal value for n is 3, returning requested palette with 3 different levels
minimal value for n is 3, returning requested palette with 3 different levels
minimal value for n is 3, returning requested palette with 3 different levels
minimal value for n is 3, returning requested palette with 3 different levels
plotScalingData %>% dplyr::select(-CommunitySeq)
What happens if we try to plot a plane?
Check with dataset as a part of the model. (They should not be, as they are generated by the same code, but different seeds.)
plotSamplingDataLM1 <- lm(
log(CommunitySize) ~ log(Basals) + log(Consumers) + Dataset,
data = plotScalingData
)
summary(plotSamplingDataLM1)
Call:
lm(formula = log(CommunitySize) ~ log(Basals) + log(Consumers) +
Dataset, data = plotScalingData)
Residuals:
Min 1Q Median 3Q Max
-0.83712 -0.17860 0.01358 0.20886 0.78899
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.91641 0.11947 7.670 5.05e-11 ***
log(Basals) 0.23818 0.02014 11.825 < 2e-16 ***
log(Consumers) 0.03652 0.01856 1.968 0.0528 .
Dataset2021-05 0.07433 0.07197 1.033 0.3050
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.3089 on 75 degrees of freedom
Multiple R-squared: 0.677, Adjusted R-squared: 0.664
F-statistic: 52.39 on 3 and 75 DF, p-value: < 2.2e-16
Datasets do not look to have a statistically significant effect. Without comparing the models (would need to do in any publication), the model without the dataset effect is:
plotSamplingDataLM2 <- lm(
log(CommunitySize) ~ log(Basals) + log(Consumers),
data = plotScalingData
)
summary(plotSamplingDataLM2)
Call:
lm(formula = log(CommunitySize) ~ log(Basals) + log(Consumers),
data = plotScalingData)
Residuals:
Min 1Q Median 3Q Max
-0.86732 -0.17301 -0.02186 0.20156 0.74221
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.93258 0.11850 7.870 1.95e-11 ***
log(Basals) 0.24312 0.01957 12.421 < 2e-16 ***
log(Consumers) 0.03800 0.01851 2.053 0.0435 *
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.309 on 76 degrees of freedom
Multiple R-squared: 0.6724, Adjusted R-squared: 0.6638
F-statistic: 77.99 on 2 and 76 DF, p-value: < 2.2e-16
Adding this to the plot:
# E.g. https://stackoverflow.com/a/38533046
plotSamplingDataLM2Res <- 3
plotSamplingDataLM2AxisX <- sort(unique(plotScalingData$Basals))
# seq(min(plotScalingData$Basals),
# max(plotScalingData$Basals),
# by = plotSamplingDataLM2Res)
plotSamplingDataLM2AxisY <- sort(unique(plotScalingData$Consumers))
# seq(min(plotScalingData$Consumers),
# max(plotScalingData$Consumers),
# by = plotSamplingDataLM2Res)
plotSamplingDataLM2Surf <- expand.grid(
Basals = plotSamplingDataLM2AxisX,
Consumers = plotSamplingDataLM2AxisY
)
plotSamplingDataLM2Surf$CommunitySize <- exp(predict.lm(
plotSamplingDataLM2, newdata = plotSamplingDataLM2Surf
))
plotSamplingDataLM2Surf <- reshape2::acast( # Cast to array/matrix
plotSamplingDataLM2Surf, Basals ~ Consumers, value.var = "CommunitySize"
)
plotScaling %>% plotly::add_trace(z = plotSamplingDataLM2Surf,
x = plotSamplingDataLM2AxisX,
y = plotSamplingDataLM2AxisY,
type = "surface")
minimal value for n is 3, returning requested palette with 3 different levels
minimal value for n is 3, returning requested palette with 3 different levels
minimal value for n is 3, returning requested palette with 3 different levels
minimal value for n is 3, returning requested palette with 3 different levels
Not particularly convincing, but it does agree with general ideas (consumers less important than basals).
plotSamplingDataLM3 <- lm(
log(CommunitySize) ~ log(Basals) + log(Consumers) - 1,
data = plotScalingData
)
summary(plotSamplingDataLM3)
Call:
lm(formula = log(CommunitySize) ~ log(Basals) + log(Consumers) -
1, data = plotScalingData)
Residuals:
Min 1Q Median 3Q Max
-0.80814 -0.13633 0.05056 0.40151 1.03117
Coefficients:
Estimate Std. Error t value Pr(>|t|)
log(Basals) 0.33969 0.02041 16.642 < 2e-16 ***
log(Consumers) 0.14852 0.01614 9.202 4.89e-14 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.4136 on 77 degrees of freedom
Multiple R-squared: 0.9598, Adjusted R-squared: 0.9587
F-statistic: 918.9 on 2 and 77 DF, p-value: < 2.2e-16
# E.g. https://stackoverflow.com/a/38533046
plotSamplingDataLM3Res <- 3
plotSamplingDataLM3AxisX <- sort(unique(plotScalingData$Basals))
# seq(min(plotScalingData$Basals),
# max(plotScalingData$Basals),
# by = plotSamplingDataLM2Res)
plotSamplingDataLM3AxisY <- sort(unique(plotScalingData$Consumers))
# seq(min(plotScalingData$Consumers),
# max(plotScalingData$Consumers),
# by = plotSamplingDataLM2Res)
plotSamplingDataLM3Surf <- expand.grid(
Basals = plotSamplingDataLM3AxisX,
Consumers = plotSamplingDataLM3AxisY
)
plotSamplingDataLM3Surf$CommunitySize <- exp(predict.lm(
plotSamplingDataLM3, newdata = plotSamplingDataLM3Surf
))
plotSamplingDataLM3Surf <- reshape2::acast( # Cast to array/matrix
plotSamplingDataLM3Surf, Basals ~ Consumers, value.var = "CommunitySize"
)
plotScaling %>% plotly::add_trace(z = plotSamplingDataLM3Surf,
x = plotSamplingDataLM3AxisX,
y = plotSamplingDataLM3AxisY,
type = "surface")
minimal value for n is 3, returning requested palette with 3 different levels
minimal value for n is 3, returning requested palette with 3 different levels
minimal value for n is 3, returning requested palette with 3 different levels
minimal value for n is 3, returning requested palette with 3 different levels
How do they interact with each other (on islands)?
# > runif(1) * 1E8
# [1] 82598679
set.seed(82598679)
mats <- list()
poolsall <- list() # name pools used in save data; be careful!
for (i in 1:length(dirViking)) {
temp <- load(file.path(
dirViking[i],
paste0("LawMorton1996-NumericalPoolCommunityScaling-PoolMats",
if (i > 1) i else "",
".RDS")
))
mats[[i]] <- eval(parse(text = temp[1]))
poolsall[[i]] <- eval(parse(text = temp[2]))
}
pools <- poolsall
oldCandidateData <- load(file.path(getwd(), "candidateDataSoFar.Rdata"))
oldCandidateData <- eval(parse(text = oldCandidateData))
candidateData <- plotScalingData %>% dplyr::group_by(
CombnNum, Dataset
) %>% dplyr::mutate(
OtherSteadyStates = dplyr::n() - 1
) %>% dplyr::filter(
OtherSteadyStates > 0
)
candidateData %>% dplyr::select(-CommunitySeq)
# First, check if it is in the paramFrame.
# Second, check if it is in the saved data from the previous.
# Otherwise, ignore it, we'll figure out what it is and why it is missing later.
candidateData$CommunityAbund <- ""
for (r in 1:nrow(candidateData)) {
# ID 1:4 are used to identify paramFrame, 5 used to identify abundance
ID <- candidateData[r, 1:6]
paramFrameRow <- paramFrame %>% dplyr::filter(
CombnNum == ID$CombnNum,
Basals == ID$Basals,
Consumers == ID$Consumers,
Dataset == ID$Dataset
)
if (is.list(paramFrameRow$EndStateAbundance[[1]])) {
entry <- which(ID$Communities == names(paramFrameRow$EndStateAbundance[[1]]))
if (length(entry)) {
candidateData$CommunityAbund[r] <- paramFrameRow$EndStateAbundance[[1]][[entry]]
next()
}
}
if (ID$Dataset == "2021-04") {
oldCandDatRow <- oldCandidateData %>% dplyr::filter(
CombnNum == ID$CombnNum,
Basals == ID$Basals,
Consumers == ID$Consumers,
Communities == ID$Communities
)
if (nrow(oldCandDatRow) > 0) {
if (oldCandDatRow$CommunityAbund != "") {
candidateData$CommunityAbund[r] <- oldCandDatRow$CommunityAbund
}
}
}
}
candidateData <- candidateData %>% dplyr::filter(CommunityAbund != "",
CommunityAbund != "Failure")
candidateData$CommunityProd <- NA
for (r in 1:nrow(candidateData)) {
candidateData$CommunityProd[r] <- with(
candidateData[r, ],
RMTRCode2::Productivity(
Pool = pools[[DatasetID]][[CombnNum]],
InteractionMatrix = mats[[DatasetID]][[CombnNum]],
Community = Communities,
Populations = CommunityAbund
)
)
}
islandFUN <- function(i, dat, pool, mat, dmat) {
temp <- dat[i, ]
RMTRCode2::IslandDynamics(
Pool = pool,
InteractionMatrix = mat,
Communities = c(
list(temp$Communities[1]),
rep("", nrow(dmat) - 2),
temp$Communities[2]
),
Populations = c(
list(temp$CommunityAbund[1]),
rep("", nrow(dmat) - 2),
list(temp$CommunityAbund[2])
),
DispersalPool = 0.0001,
DispersalIsland = dmat,
)
}
# For each group-dataset,
# For each pair,
# Run Island Dynamics,
# Save the result with its pairing
candidateData$TotalID <- paste(candidateData$CombnNum, candidateData$DatasetID)
islandInteractionsOneTwo <- list()
for (grp in unique(candidateData$TotalID)) {
candidateDataSubset <- candidateData %>% dplyr::filter(TotalID == grp)
if (nrow(candidateDataSubset) == 1) next()
pairingResults <- combn(
nrow(candidateDataSubset), 2,
islandFUN,
dat = candidateDataSubset,
pool = pools[[
candidateDataSubset$DatasetID[1]
]][[candidateDataSubset$CombnNum[1]]],
mat = mats[[
candidateDataSubset$DatasetID[1]
]][[candidateDataSubset$CombnNum[1]]],
dmat = matrix(c(0, 1, 1, 0), nrow = 2, ncol = 2),
simplify = FALSE
)
pairingResults <- lapply(
pairingResults, function(mat, isles) {
mat <- mat[nrow(mat), -1]
retVal <- list()
species <- length(mat) / isles
for (i in 1:isles) {
retVal[[i]] <- mat[((i - 1) * species + 1) : (i * species)]
}
retVal
},
isles = 2
)
islandInteractionsOneTwo[[grp]] <- pairingResults
}
islandInteractionsOneEmptyTwo <- list()
for (grp in unique(candidateData$TotalID)) {
candidateDataSubset <- candidateData %>% dplyr::filter(TotalID == grp)
if (nrow(candidateDataSubset) == 1) next()
pairingResults <- combn(
nrow(candidateDataSubset), 2,
islandFUN,
dat = candidateDataSubset,
pool = pools[[
candidateDataSubset$DatasetID[1]
]][[candidateDataSubset$CombnNum[1]]],
mat = mats[[
candidateDataSubset$DatasetID[1]
]][[candidateDataSubset$CombnNum[1]]],
dmat = matrix(c(
0, 1, 0, # Island 2 -> 1
1, 0, 1, # Island 1 -> 2, Island 3 -> 2
0, 1, 0 # Island 2 -> 3
), nrow = 3, ncol = 3, byrow = TRUE),
simplify = FALSE
)
pairingResults <- lapply(
pairingResults, function(mat, isles) {
mat <- mat[nrow(mat), -1]
retVal <- list()
species <- length(mat) / isles
for (i in 1:isles) {
retVal[[i]] <- mat[((i - 1) * species + 1) : (i * species)]
}
retVal
},
isles = 3
)
islandInteractionsOneEmptyTwo[[grp]] <- pairingResults
}
# Format of table should be:
# ID, Community 1, Community 2, Outcomes 1-2, Outcomes 1-0-2
# For outcomes, species presence will be used.
communities <- NULL
totalCommunities <- NULL
for (grp in unique(candidateData$TotalID)) {
candidateDataSubset <- candidateData %>% dplyr::filter(TotalID == grp)
if (nrow(candidateDataSubset) > 1) {
newCommunities <- combn(
candidateDataSubset$Communities, 2,
)
communities <- c(communities, newCommunities)
totalCommunities <- c(
totalCommunities,
toString(sort(unique(unlist(lapply(newCommunities,
RMTRCode2::CsvRowSplit)))))
)
}
}
islandInteractionsOneTwoWhich <- unlist(lapply(
seq_along(islandInteractionsOneTwo), function(i, x, tC) {
lapply(x[[i]], function(y, tC) {
lapply(y, function(z, tC) {
toString(RMTRCode2::CsvRowSplit(tC)[which(z > 1E-6)])
}, tC = tC)
},
tC = tC[i])
},
x = islandInteractionsOneTwo,
tC = totalCommunities))
islandInteractionsOneEmptyTwoWhich <- unlist(lapply(
seq_along(islandInteractionsOneEmptyTwo), function(i, x, tC) {
lapply(x[[i]], function(y, tC) {
lapply(y, function(z, tC) {
toString(RMTRCode2::CsvRowSplit(tC)[which(z > 1E-6)])
}, tC = tC)
},
tC = tC[i])
},
x = islandInteractionsOneEmptyTwo,
tC = totalCommunities))
islandInteractionResults <- data.frame(
DatasetID = rep(names(islandInteractionsOneTwo),
unlist(lapply(islandInteractionsOneTwo, length))),
Community1 = communities[seq(from = 1, to = length(communities), by = 2)],
Community2 = communities[seq(from = 2, to = length(communities), by = 2)],
OutcomeWOEmpty_Island1 = islandInteractionsOneTwoWhich[
seq(from = 1, to = length(islandInteractionsOneTwoWhich), by = 2)],
OutcomeWOEmpty_Island2 = islandInteractionsOneTwoWhich[
seq(from = 1, to = length(islandInteractionsOneTwoWhich), by = 2)],
OutcomeWEmpty_Island1 = islandInteractionsOneEmptyTwoWhich[
seq(from = 1, to = length(islandInteractionsOneEmptyTwoWhich), by = 3)],
OutcomeWEmpty_Island2 = islandInteractionsOneEmptyTwoWhich[
seq(from = 1, to = length(islandInteractionsOneEmptyTwoWhich), by = 3)],
OutcomeWEmpty_Island3 = islandInteractionsOneEmptyTwoWhich[
seq(from = 1, to = length(islandInteractionsOneEmptyTwoWhich), by = 3)]
)
islandInteractionResults
islandInteractionResults %>% dplyr::mutate(
C1WOInvaded = Community1 != OutcomeWOEmpty_Island1,
C2WOInvaded = Community2 != OutcomeWOEmpty_Island2,
C1WInvaded = Community1 != OutcomeWEmpty_Island1,
C2WInvaded = Community2 != OutcomeWEmpty_Island3,
StalemateWO = !C1WOInvaded & !C2WOInvaded,
StalemateW = !C1WInvaded & !C2WInvaded,
HybridWO = C1WOInvaded & C2WOInvaded,
HybridW = C1WInvaded & C2WInvaded,
) %>% dplyr::select(-dplyr::starts_with("Outcome"))
LS0tDQp0aXRsZTogIlZpa2luZyBSZXN1bHRzLCAyMDIxLTA1Ig0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KLS0tDQoNCmBgYHtyIGxpYnN9DQojIENoZWNrIHJlcXVpc2l0ZSBwYWNrYWdlcyBhcmUgaW5zdGFsbGVkLg0KcGFja2FnZXMgPC0gYygNCiAgInBsb3RseSIsIA0KICAiZHBseXIiDQopDQpmb3IgKHBrZyBpbiBwYWNrYWdlcykgew0KICBsaWJyYXJ5KHBrZywgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQ0KfQ0KYGBgDQoNCiMgV2hhdCBkbyB0aGUgcmVzdWx0cyBsb29rIGxpa2U/DQpgYGB7ciBkaXJzfQ0KZGlyVmlraW5nIDwtIGMoDQogIGZpbGUucGF0aCgNCiAgICBnZXR3ZCgpLCAiTENBQl9MYXdNb3J0b24xOTk2LU51bWVyaWNhbFBvb2xDb21tdW5pdHlTY2FsaW5nIg0KICApLA0KICBmaWxlLnBhdGgoDQogICAgZ2V0d2QoKSwgIkxDQUJfTGF3TW9ydG9uMTk5Ni1OdW1lcmljYWxQb29sQ29tbXVuaXR5U2NhbGluZzIiDQogICkNCikNCmRpclZpa2luZ1Jlc3VsdHMgPC0gZmlsZS5wYXRoKA0KICBkaXJWaWtpbmcsIGMoInJlc3VsdHMtMjAyMS0wNCIsICJzYXZlLTIwMjEtMDUtMTAiKSAjIExhdHRlciBub3QgMTAwJSB5ZXQuDQopDQpyZXN1bHRGb3JtYXQgPC0gcGFzdGUwKA0KICAicnVuLSIsIA0KICAiJWQiLCAjIENvbWJpbmF0aW9uIE51bWJlciwgb3IgQ29tYm5OdW0uDQogICItIiwgDQogICIlcyIsICMgUnVuIFNlZWQuDQogICIuUkRTIg0KKQ0KYGBgDQoNCiMjIDIwMjEtMDQgRGF0YQ0KYGBge3IgcGFyYW1zfQ0KIyBDb3BpZWQgZnJvbSBMYXdNb3J0b24xOTk2LU51bWVyaWNhbFBvb2xDb21tdW5pdHlTY2FsaW5nLUNhbGN1bGF0aW9uLlINCiMgVE9ETzogSW4gdGhlIGZ1dHVyZSwgbWFrZSB0aGlzIGEgc2VwYXJhdGUgZmlsZSB0aGF0IGV2ZXJ5b25lIGNhbiBjYWxsLi4uDQpzZXQuc2VlZCgzODQyNzA0MikNCg0KYmFzYWwgPC0gYygzLCAxMCwgMzAsIDEwMCwgMzAwLCAxMDAwKQ0KY29uc3VtZXIgPC0gYygzLCAxMCwgMzAsIDEwMCwgMzAwLCAxMDAwKSAqIDINCmV2ZW50cyA8LSAobWF4KGJhc2FsKSArIG1heChjb25zdW1lcikpICogMg0KcnVucyA8LSAxMDANCg0KbG9nQm9keVNpemUgPC0gYygtMiwgLTEsIC0xLCAxKSAjIE1vcnRvbiBhbmQgTGF3IDE5OTcgdmVyc2lvbi4NCnBhcmFtZXRlcnMgPC0gYygwLjAxLCAxMCwgMC41LCAwLjIsIDEwMCwgMC4xKQ0KDQojIE5lZWQgdG8gcmVydW4gc2VlZHNQcmVwIHRvIGdldCB0aGUgcmFuZG9tIG51bWJlciBnZW5lcmF0aW9uIHJpZ2h0IGZvciBzZWVkc1J1bg0Kc2VlZHNQcmVwIDwtIHJ1bmlmKDIgKiBsZW5ndGgoYmFzYWwpICogbGVuZ3RoKGNvbnN1bWVyKSkgKiAxRTgNCnNlZWRzUnVuIDwtIHJ1bmlmKHJ1bnMgKiBsZW5ndGgoYmFzYWwpICogbGVuZ3RoKGNvbnN1bWVyKSkgKiAxRTgNCmBgYA0KDQpgYGB7ciBvcmdhbmlzZVBhcmFtc30NCnBhcmFtRnJhbWUgPC0gd2l0aChsaXN0KA0KICBiID0gcmVwKGJhc2FsLCB0aW1lcyA9IGxlbmd0aChjb25zdW1lcikpLA0KICBjID0gcmVwKGNvbnN1bWVyLCBlYWNoID0gbGVuZ3RoKGJhc2FsKSksDQogIHMxID0gc2VlZHNQcmVwWzE6KGxlbmd0aChiYXNhbCkgKiBsZW5ndGgoY29uc3VtZXIpKV0sDQogIHMyID0gc2VlZHNQcmVwWw0KICAgIChsZW5ndGgoYmFzYWwpICogbGVuZ3RoKGNvbnN1bWVyKSArIDEpOigNCiAgICAgIDIgKiBsZW5ndGgoYmFzYWwpICogbGVuZ3RoKGNvbnN1bWVyKSkNCiAgXSwNCiAgc1IgPSBzZWVkc1J1bg0KKSwgew0KICB0ZW1wIDwtIGRhdGEuZnJhbWUoDQogICAgQ29tYm5OdW0gPSAwLA0KICAgIEJhc2FscyA9IGIsDQogICAgQ29uc3VtZXJzID0gYywNCiAgICBTZWVkUG9vbCA9IHMxLA0KICAgIFNlZWRNYXQgPSBzMiwNCiAgICBTZWVkUnVucyA9ICIiLA0KICAgIFNlZWRSdW5zTnVtID0gMCwNCiAgICBFbmRTdGF0ZXMgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKGIpKSksDQogICAgRW5kU3RhdGVzTnVtID0gMCwNCiAgICBFbmRTdGF0ZVNpemVzID0gSShyZXAobGlzdCgiIiksIGxlbmd0aChiKSkpLA0KICAgIEVuZFN0YXRlU2l6ZXNOdW0gPSBOQSwNCiAgICBFbmRTdGF0ZUFzc2VtYmx5ID0gSShyZXAobGlzdCgiIiksIGxlbmd0aChiKSkpLA0KICAgIEVuZFN0YXRlQWJ1bmRhbmNlID0gSShyZXAobGlzdCgiIiksIGxlbmd0aChiKSkpLA0KICAgIERhdGFzZXQgPSAiMjAyMS0wNCIsDQogICAgRGF0YXNldElEID0gMSwNCiAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UNCiAgKQ0KICBmb3IgKGkgaW4gMTpucm93KHRlbXApKSB7DQogICAgc2VlZHMgPC0gc1JbKChpIC0gMSkgKiBydW5zICsgMSkgOiAoaSAqIHJ1bnMpXQ0KICAgIHRlbXAkU2VlZFJ1bnNbaV0gPC0gdG9TdHJpbmcoc2VlZHMpICMgQ1NWDQogICAgdGVtcCRTZWVkUnVuc051bVtpXSA8LSBsZW5ndGgoc2VlZHMpDQogIH0NCiAgdGVtcCRDb21ibk51bSA8LSAxOm5yb3codGVtcCkNCiAgdGVtcA0KfSkNCmBgYA0KDQpgYGB7ciBsb2FkUmVzdWx0c30NCiMgTm90ZTogbiArIDIgZW5kIHN0YXRlcy4gRmFpbHVyZSB0byBmaW5pc2gsIGZhaWx1cmUgdG8gb2J0YWluIHN0YXRlLCBhbmQgc3RhdGUuDQpmb3IgKGkgaW4gMTpucm93KHBhcmFtRnJhbWUpKSB7DQogIHJlc3VsdHNMaXN0IDwtIGxpc3QoDQogICAgIk5vIFJ1biIgPSAwLA0KICAgICJObyBTdGF0ZSIgPSAwDQogICkNCiAgcmVzdWx0c1NpemUgPC0gbGlzdCgNCiAgICAiMCIgPSAwDQogICkNCiAgcmVzdWx0c0Fzc2VtYmx5IDwtIGxpc3QoDQogICAgIk5vIFJ1biIgPSBkYXRhLmZyYW1lKCksDQogICAgIk5vIFN0YXRlIiA9IGRhdGEuZnJhbWUoKQ0KICApDQogIHNlZWRzIDwtIHVubGlzdChzdHJzcGxpdChwYXJhbUZyYW1lJFNlZWRSdW5zW2ldLCAnLCAnKSkNCiAgZm9yIChzZWVkIGluIHNlZWRzKSB7DQogICAgZmlsZU5hbWUgPC0gZmlsZS5wYXRoKA0KICAgICAgZGlyVmlraW5nUmVzdWx0c1twYXJhbUZyYW1lJERhdGFzZXRJRFtpXV0sDQogICAgICBzcHJpbnRmKHJlc3VsdEZvcm1hdCwgcGFyYW1GcmFtZSRDb21ibk51bVtpXSwgc2VlZCkNCiAgICApDQogICAgDQogICAgaWYgKGZpbGUuZXhpc3RzKGZpbGVOYW1lKSkgew0KICAgICAgdGVtcCA8LSBsb2FkKGZpbGVOYW1lKQ0KICAgICAgdGVtcCA8LSBldmFsKHBhcnNlKHRleHQgPSB0ZW1wKSkgIyBHZXQgb2JqZWN0cy4NCiAgICAgIA0KICAgICAgaWYgKGlzLmRhdGEuZnJhbWUodGVtcCkpIHsNCiAgICAgICAgY29tbXVuaXR5IDwtIHRvU3RyaW5nKA0KICAgICAgICAgIHRlbXBbW25jb2wodGVtcCldXVtbbnJvdyh0ZW1wKV1dDQogICAgICAgICkNCiAgICAgICAgc2l6ZSA8LSB0b1N0cmluZyhsZW5ndGgodGVtcFtbbmNvbCh0ZW1wKV1dW1tucm93KHRlbXApXV0pKQ0KICAgICAgICANCiAgICAgICAgaWYgKGNvbW11bml0eSA9PSAiIikgew0KICAgICAgICAgIHJlc3VsdHNMaXN0JGBObyBTdGF0ZWAgPC0gcmVzdWx0c0xpc3QkYE5vIFN0YXRlYCArIDENCiAgICAgICAgICByZXN1bHRzU2l6ZSRgMGAgPC0gcmVzdWx0c1NpemUkYDBgICsgMQ0KICAgICAgICAgIA0KICAgICAgICB9IGVsc2UgaWYgKGNvbW11bml0eSAlaW4lIG5hbWVzKHJlc3VsdHNMaXN0KSkgew0KICAgICAgICAgIHJlc3VsdHNMaXN0W1tjb21tdW5pdHldXSA8LSByZXN1bHRzTGlzdFtbY29tbXVuaXR5XV0gKyAxDQogICAgICAgICAgcmVzdWx0c1NpemVbW3NpemVdXSA8LSByZXN1bHRzU2l6ZVtbc2l6ZV1dICsgMQ0KICAgICAgICAgIA0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgIHJlc3VsdHNMaXN0W1tjb21tdW5pdHldXSA8LSAxDQogICAgICAgICAgcmVzdWx0c0Fzc2VtYmx5W1tjb21tdW5pdHldXSA8LSB0ZW1wDQogICAgICAgICAgDQogICAgICAgICAgaWYgKHNpemUgJWluJSByZXN1bHRzU2l6ZSkgew0KICAgICAgICAgICAgcmVzdWx0c1NpemVbW3NpemVdXSA8LSByZXN1bHRzU2l6ZVtbc2l6ZV1dICsgMQ0KICAgICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgICByZXN1bHRzU2l6ZVtbc2l6ZV1dIDwtIDENCiAgICAgICAgICB9DQogICAgICAgIH0NCiAgICAgIH0gZWxzZSB7DQogICAgICAgIHJlc3VsdHNMaXN0JGBObyBTdGF0ZWAgPC0gcmVzdWx0c0xpc3QkYE5vIFN0YXRlYCArIDENCiAgICAgICAgcmVzdWx0c1NpemUkYDBgIDwtIHJlc3VsdHNTaXplJGAwYCArIDENCiAgICAgIH0NCiAgICB9IGVsc2Ugew0KICAgICAgcmVzdWx0c0xpc3QkYE5vIFJ1bmAgPC0gcmVzdWx0c0xpc3QkYE5vIFJ1bmAgKyAxDQogICAgICByZXN1bHRzU2l6ZSRgMGAgPC0gcmVzdWx0c1NpemUkYDBgICsgMQ0KICAgIH0NCiAgfQ0KICANCiAgcGFyYW1GcmFtZSRFbmRTdGF0ZXNbW2ldXSA8LSByZXN1bHRzTGlzdA0KICBwYXJhbUZyYW1lJEVuZFN0YXRlc051bVtpXSA8LSBsZW5ndGgocmVzdWx0c0xpc3QpIC0gMiAjICEgTm8gU3RhdGUsIE5vIFJ1bg0KICBwYXJhbUZyYW1lJEVuZFN0YXRlU2l6ZXNbW2ldXSA8LSByZXN1bHRzU2l6ZQ0KICBwYXJhbUZyYW1lJEVuZFN0YXRlU2l6ZXNOdW1baV0gPC0gbGVuZ3RoKHJlc3VsdHNTaXplKSAtIDEgIyAhIDANCiAgcGFyYW1GcmFtZSRFbmRTdGF0ZUFzc2VtYmx5W1tpXV0gPC0gcmVzdWx0c0Fzc2VtYmx5DQp9DQpgYGANCg0KIyMgMjAyMS0wNSBEYXRhDQpgYGB7ciBvcmdhbmlzZVBhcmFtczJ9DQpzb3VyY2UoDQogIGZpbGUucGF0aChnZXR3ZCgpLCANCiAgICAgICAgICAgICJMYXdNb3J0b24xOTk2LU51bWVyaWNhbFBvb2xDb21tdW5pdHlTY2FsaW5nLVNldHRpbmdzMi5SIikNCikNCg0Kb2xkTnJvdyA8LSBucm93KHBhcmFtRnJhbWUpDQoNCnBhcmFtRnJhbWUgPC0gcmJpbmQocGFyYW1GcmFtZSwgd2l0aChsaXN0KA0KICBiID0gcmVwKGJhc2FsLCB0aW1lcyA9IGxlbmd0aChjb25zdW1lcikpLA0KICBjID0gcmVwKGNvbnN1bWVyLCBlYWNoID0gbGVuZ3RoKGJhc2FsKSksDQogIHMxID0gc2VlZHNQcmVwWzE6KGxlbmd0aChiYXNhbCkgKiBsZW5ndGgoY29uc3VtZXIpKV0sDQogIHMyID0gc2VlZHNQcmVwWw0KICAgIChsZW5ndGgoYmFzYWwpICogbGVuZ3RoKGNvbnN1bWVyKSArIDEpOigNCiAgICAgIDIgKiBsZW5ndGgoYmFzYWwpICogbGVuZ3RoKGNvbnN1bWVyKSkNCiAgXSwNCiAgc1IgPSBzZWVkc1J1bg0KKSwgew0KICB0ZW1wIDwtIGRhdGEuZnJhbWUoDQogICAgQ29tYm5OdW0gPSAwLA0KICAgIEJhc2FscyA9IGIsDQogICAgQ29uc3VtZXJzID0gYywNCiAgICBTZWVkUG9vbCA9IHMxLA0KICAgIFNlZWRNYXQgPSBzMiwNCiAgICBTZWVkUnVucyA9ICIiLA0KICAgIFNlZWRSdW5zTnVtID0gMCwNCiAgICBFbmRTdGF0ZXMgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKGIpKSksDQogICAgRW5kU3RhdGVzTnVtID0gMCwNCiAgICBFbmRTdGF0ZVNpemVzID0gSShyZXAobGlzdCgiIiksIGxlbmd0aChiKSkpLA0KICAgIEVuZFN0YXRlU2l6ZXNOdW0gPSBOQSwNCiAgICBFbmRTdGF0ZUFzc2VtYmx5ID0gSShyZXAobGlzdCgiIiksIGxlbmd0aChiKSkpLA0KICAgIEVuZFN0YXRlQWJ1bmRhbmNlID0gSShyZXAobGlzdCgiIiksIGxlbmd0aChiKSkpLA0KICAgIERhdGFzZXQgPSAiMjAyMS0wNSIsDQogICAgRGF0YXNldElEID0gMiwNCiAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UNCiAgKQ0KICBmb3IgKGkgaW4gMTpucm93KHRlbXApKSB7DQogICAgc2VlZHMgPC0gc1JbKChpIC0gMSkgKiBydW5zICsgMSkgOiAoaSAqIHJ1bnMpXQ0KICAgIHRlbXAkU2VlZFJ1bnNbaV0gPC0gdG9TdHJpbmcoc2VlZHMpICMgQ1NWDQogICAgdGVtcCRTZWVkUnVuc051bVtpXSA8LSBsZW5ndGgoc2VlZHMpDQogIH0NCiAgdGVtcCRDb21ibk51bSA8LSAxOm5yb3codGVtcCkNCiAgdGVtcA0KfSkNCikNCmBgYA0KDQpgYGB7ciBsb2FkUmVzdWx0czJ9DQojIE5vdGU6IG4gKyAyIGVuZCBzdGF0ZXMuIEZhaWx1cmUgdG8gZmluaXNoLCBmYWlsdXJlIHRvIG9idGFpbiBzdGF0ZSwgYW5kIHN0YXRlLg0KIyBNb2RpZmllZCBmcm9tIGFib3ZlLCBidXQgd2l0aCB0aGUgYWJ1bmRhbmNlIHJlY29yZGVkLg0KZm9yIChpIGluIChvbGROcm93ICsgMSk6bnJvdyhwYXJhbUZyYW1lKSkgew0KICByZXN1bHRzTGlzdCA8LSBsaXN0KA0KICAgICJObyBSdW4iID0gMCwNCiAgICAiTm8gU3RhdGUiID0gMA0KICApDQogIHJlc3VsdHNTaXplIDwtIGxpc3QoDQogICAgIjAiID0gMA0KICApDQogIHJlc3VsdHNBc3NlbWJseSA8LSBsaXN0KA0KICAgICJObyBSdW4iID0gZGF0YS5mcmFtZSgpLA0KICAgICJObyBTdGF0ZSIgPSBkYXRhLmZyYW1lKCkNCiAgKQ0KICByZXN1bHRzQWJ1bmQgPC0gbGlzdCgNCiAgICAiTm8gUnVuIiA9ICIiLA0KICAgICJObyBTdGF0ZSIgPSAiIg0KICApDQogIHNlZWRzIDwtIHVubGlzdChzdHJzcGxpdChwYXJhbUZyYW1lJFNlZWRSdW5zW2ldLCAnLCAnKSkNCiAgZm9yIChzZWVkIGluIHNlZWRzKSB7DQogICAgZmlsZU5hbWUgPC0gZmlsZS5wYXRoKA0KICAgICAgZGlyVmlraW5nUmVzdWx0c1twYXJhbUZyYW1lJERhdGFzZXRJRFtpXV0sDQogICAgICBzcHJpbnRmKHJlc3VsdEZvcm1hdCwgcGFyYW1GcmFtZSRDb21ibk51bVtpXSwgc2VlZCkNCiAgICApDQogICAgDQogICAgaWYgKGZpbGUuZXhpc3RzKGZpbGVOYW1lKSkgew0KICAgICAgdGVtcCA8LSBsb2FkKGZpbGVOYW1lKQ0KICAgICAgdGVtcCA8LSBldmFsKHBhcnNlKHRleHQgPSB0ZW1wKSkgIyBHZXQgb2JqZWN0cy4NCiAgICAgIA0KICAgICAgaWYgKGlzLmxpc3QodGVtcCkgJiYgIlJlc3VsdCIgJWluJSBuYW1lcyh0ZW1wKSkgew0KICAgICAgICANCiAgICAgICAgaWYgKGlzLmRhdGEuZnJhbWUodGVtcCRSZXN1bHQpKQ0KICAgICAgICAgIGNvbW11bml0eSA8LSB0ZW1wJFJlc3VsdCRDb21tdW5pdHlbW25yb3codGVtcCRSZXN1bHQpXV0NCiAgICAgICAgZWxzZSANCiAgICAgICAgICBjb21tdW5pdHkgPC0gdGVtcCRSZXN1bHQNCiAgICAgICAgDQogICAgICAgIHNpemUgPC0gdG9TdHJpbmcobGVuZ3RoKGNvbW11bml0eSkpDQogICAgICAgIA0KICAgICAgICBpZiAoY29tbXVuaXR5WzFdICE9ICIiKSANCiAgICAgICAgICBhYnVuZCA8LSB0b1N0cmluZyh0ZW1wJEFidW5kW2NvbW11bml0eSArIDFdKQ0KICAgICAgICBlbHNlIA0KICAgICAgICAgIGFidW5kIDwtICIiDQogICAgICAgIA0KICAgICAgICBjb21tdW5pdHkgPC0gdG9TdHJpbmcoY29tbXVuaXR5KQ0KICAgICAgICANCiAgICAgICAgaWYgKGNvbW11bml0eSA9PSAiIikgew0KICAgICAgICAgIHJlc3VsdHNMaXN0JGBObyBTdGF0ZWAgPC0gcmVzdWx0c0xpc3QkYE5vIFN0YXRlYCArIDENCiAgICAgICAgICByZXN1bHRzU2l6ZSRgMGAgPC0gcmVzdWx0c1NpemUkYDBgICsgMQ0KICAgICAgICAgIA0KICAgICAgICB9IGVsc2UgaWYgKGNvbW11bml0eSAlaW4lIG5hbWVzKHJlc3VsdHNMaXN0KSkgew0KICAgICAgICAgIHJlc3VsdHNMaXN0W1tjb21tdW5pdHldXSA8LSByZXN1bHRzTGlzdFtbY29tbXVuaXR5XV0gKyAxDQogICAgICAgICAgcmVzdWx0c1NpemVbW3NpemVdXSA8LSByZXN1bHRzU2l6ZVtbc2l6ZV1dICsgMQ0KICAgICAgICAgIA0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgIHJlc3VsdHNMaXN0W1tjb21tdW5pdHldXSA8LSAxDQogICAgICAgICAgcmVzdWx0c0Fzc2VtYmx5W1tjb21tdW5pdHldXSA8LSB0ZW1wDQogICAgICAgICAgcmVzdWx0c0FidW5kW1tjb21tdW5pdHldXSA8LSBhYnVuZA0KICAgICAgICAgIA0KICAgICAgICAgIGlmIChzaXplICVpbiUgcmVzdWx0c1NpemUpIHsNCiAgICAgICAgICAgIHJlc3VsdHNTaXplW1tzaXplXV0gPC0gcmVzdWx0c1NpemVbW3NpemVdXSArIDENCiAgICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICAgcmVzdWx0c1NpemVbW3NpemVdXSA8LSAxDQogICAgICAgICAgfQ0KICAgICAgICB9DQogICAgICB9IGVsc2Ugew0KICAgICAgICByZXN1bHRzTGlzdCRgTm8gU3RhdGVgIDwtIHJlc3VsdHNMaXN0JGBObyBTdGF0ZWAgKyAxDQogICAgICAgIHJlc3VsdHNTaXplJGAwYCA8LSByZXN1bHRzU2l6ZSRgMGAgKyAxDQogICAgICB9DQogICAgfSBlbHNlIHsNCiAgICAgIHJlc3VsdHNMaXN0JGBObyBSdW5gIDwtIHJlc3VsdHNMaXN0JGBObyBSdW5gICsgMQ0KICAgICAgcmVzdWx0c1NpemUkYDBgIDwtIHJlc3VsdHNTaXplJGAwYCArIDENCiAgICB9DQogIH0NCiAgDQogIHBhcmFtRnJhbWUkRW5kU3RhdGVzW1tpXV0gPC0gcmVzdWx0c0xpc3QNCiAgcGFyYW1GcmFtZSRFbmRTdGF0ZXNOdW1baV0gPC0gbGVuZ3RoKHJlc3VsdHNMaXN0KSAtIDIgIyAhIE5vIFN0YXRlLCBObyBSdW4NCiAgcGFyYW1GcmFtZSRFbmRTdGF0ZVNpemVzW1tpXV0gPC0gcmVzdWx0c1NpemUNCiAgcGFyYW1GcmFtZSRFbmRTdGF0ZVNpemVzTnVtW2ldIDwtIGxlbmd0aChyZXN1bHRzU2l6ZSkgLSAxICMgISAwDQogIHBhcmFtRnJhbWUkRW5kU3RhdGVBc3NlbWJseVtbaV1dIDwtIHJlc3VsdHNBc3NlbWJseQ0KICBwYXJhbUZyYW1lJEVuZFN0YXRlQWJ1bmRhbmNlW1tpXV0gPC0gcmVzdWx0c0FidW5kDQp9DQpgYGANCg0KIyMgUGxvdA0KDQpgYGB7ciBwbG90M0R9DQojIFgsIFksIEJhc2FsIGFuZCBDb25zdW1lci4NCiMgWiA9IFNpemVzIG9mIHRoZSBFbmRzdGF0ZXMuDQoNCnBsb3RTY2FsaW5nRGF0YSA8LSBkYXRhLmZyYW1lKA0KICBDb21ibk51bSA9IHJlcChwYXJhbUZyYW1lJENvbWJuTnVtLCBwYXJhbUZyYW1lJEVuZFN0YXRlc051bSksDQogIEJhc2FscyA9IHJlcChwYXJhbUZyYW1lJEJhc2FscywgcGFyYW1GcmFtZSRFbmRTdGF0ZXNOdW0pLA0KICBDb25zdW1lcnMgPSByZXAocGFyYW1GcmFtZSRDb25zdW1lcnMsIHBhcmFtRnJhbWUkRW5kU3RhdGVzTnVtKSwNCiAgRGF0YXNldCA9IHJlcChwYXJhbUZyYW1lJERhdGFzZXQsIHBhcmFtRnJhbWUkRW5kU3RhdGVzTnVtKSwNCiAgRGF0YXNldElEID0gcmVwKHBhcmFtRnJhbWUkRGF0YXNldElELCBwYXJhbUZyYW1lJEVuZFN0YXRlc051bSkNCikNCg0KIyBDb21tdW5pdGllcw0KY29tbXMgPC0gdW5saXN0KGxhcHBseShwYXJhbUZyYW1lJEVuZFN0YXRlcywgbmFtZXMpKQ0KZnJlcXMgPC0gdW5saXN0KHBhcmFtRnJhbWUkRW5kU3RhdGVzKQ0KYXNtYmwgPC0gdW5saXN0KHBhcmFtRnJhbWUkRW5kU3RhdGVBc3NlbWJseSwgcmVjdXJzaXZlID0gRkFMU0UpDQphc21ibCA8LSBhc21ibFtjb21tcyAhPSAiTm8gUnVuIiAmIGNvbW1zICE9ICJObyBTdGF0ZSJdDQpmcmVxcyA8LSBmcmVxc1tjb21tcyAhPSAiTm8gUnVuIiAmIGNvbW1zICE9ICJObyBTdGF0ZSJdDQpjb21tcyA8LSBjb21tc1tjb21tcyAhPSAiTm8gUnVuIiAmIGNvbW1zICE9ICJObyBTdGF0ZSJdDQoNCmFzbWJsIDwtIGxhcHBseShhc21ibCwgZnVuY3Rpb24oZCkgew0KICBpZiAoIlJlc3VsdC5PdXRjb21lIiAlaW4lIG5hbWVzKGQpKQ0KICAgIGQgJT4lIGRwbHlyOjpmaWx0ZXIoUmVzdWx0Lk91dGNvbWUgIT0gIlR5cGUgMSAoRmFpbHVyZSkiICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgIFJlc3VsdC5PdXRjb21lICE9ICJQcmVzZW50IikNCiAgZWxzZQ0KICAgIGQkUmVzdWx0ICU+JSBkcGx5cjo6ZmlsdGVyKE91dGNvbWUgIT0gIlR5cGUgMSAoRmFpbHVyZSkiICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPdXRjb21lICE9ICJQcmVzZW50IikNCn0pDQoNCnBsb3RTY2FsaW5nRGF0YSRDb21tdW5pdGllcyA8LSBjb21tcw0KcGxvdFNjYWxpbmdEYXRhJENvbW11bml0eUZyZXEgPC0gZnJlcXMNCnBsb3RTY2FsaW5nRGF0YSRDb21tdW5pdHlTZXEgPC0gYXNtYmwNCg0KIyBDb21tdW5pdHkgU2l6ZQ0KdGVtcCA8LSB1bmxpc3QobGFwcGx5KHN0cnNwbGl0KHBsb3RTY2FsaW5nRGF0YSRDb21tdW5pdGllcywgJywnKSwgbGVuZ3RoKSkNCnBsb3RTY2FsaW5nRGF0YSRDb21tdW5pdHlTaXplIDwtIHRlbXANCg0KIyBGb3IgdXNhZ2UgYnkgdGhlIHJlYWRlci4NCg0KcGxvdFNjYWxpbmcgPC0gcGxvdGx5OjpwbG90X2x5KA0KICBwbG90U2NhbGluZ0RhdGEsDQogIHggPSB+QmFzYWxzLA0KICB5ID0gfkNvbnN1bWVycywNCiAgeiA9IH5Db21tdW5pdHlTaXplLA0KICBjb2xvciA9IH5EYXRhc2V0DQopDQoNCnBsb3RTY2FsaW5nIDwtIHBsb3RseTo6YWRkX21hcmtlcnMocGxvdFNjYWxpbmcpDQoNCnBsb3RTY2FsaW5nIDwtIHBsb3RseTo6bGF5b3V0KA0KICBwbG90U2NhbGluZywNCiAgc2NlbmUgPSBsaXN0KA0KICAgIHhheGlzID0gbGlzdCh0eXBlID0gImxvZyIpLA0KICAgIHlheGlzID0gbGlzdCh0eXBlID0gImxvZyIpDQogICkNCikNCg0KcGxvdFNjYWxpbmcNCmBgYA0KYGBge3IgcGxvdDNkRGF0YX0NCnBsb3RTY2FsaW5nRGF0YSAlPiUgZHBseXI6OnNlbGVjdCgtQ29tbXVuaXR5U2VxKQ0KYGBgDQojIyBXaGF0IGhhcHBlbnMgaWYgd2UgdHJ5IHRvIHBsb3QgYSBwbGFuZT8NCg0KQ2hlY2sgd2l0aCBkYXRhc2V0IGFzIGEgcGFydCBvZiB0aGUgbW9kZWwuIA0KKFRoZXkgc2hvdWxkIG5vdCBiZSwgYXMgdGhleSBhcmUgZ2VuZXJhdGVkIGJ5IHRoZSBzYW1lIGNvZGUsIGJ1dCBkaWZmZXJlbnQgc2VlZHMuKQ0KDQpgYGB7ciBsaW5lYXJtb2RlbDF9DQpwbG90U2FtcGxpbmdEYXRhTE0xIDwtIGxtKA0KICBsb2coQ29tbXVuaXR5U2l6ZSkgfiBsb2coQmFzYWxzKSArIGxvZyhDb25zdW1lcnMpICsgRGF0YXNldCwgDQogIGRhdGEgPSBwbG90U2NhbGluZ0RhdGENCikNCnN1bW1hcnkocGxvdFNhbXBsaW5nRGF0YUxNMSkNCmBgYA0KDQpEYXRhc2V0cyBkbyBub3QgbG9vayB0byBoYXZlIGEgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBlZmZlY3QuDQpXaXRob3V0IGNvbXBhcmluZyB0aGUgbW9kZWxzICh3b3VsZCBuZWVkIHRvIGRvIGluIGFueSBwdWJsaWNhdGlvbiksIHRoZSBtb2RlbA0Kd2l0aG91dCB0aGUgZGF0YXNldCBlZmZlY3QgaXM6DQoNCmBgYHtyIGxpbmVhcm1vZGVsMn0NCnBsb3RTYW1wbGluZ0RhdGFMTTIgPC0gbG0oDQogIGxvZyhDb21tdW5pdHlTaXplKSB+IGxvZyhCYXNhbHMpICsgbG9nKENvbnN1bWVycyksIA0KICBkYXRhID0gcGxvdFNjYWxpbmdEYXRhDQopDQpzdW1tYXJ5KHBsb3RTYW1wbGluZ0RhdGFMTTIpDQpgYGANCg0KQWRkaW5nIHRoaXMgdG8gdGhlIHBsb3Q6DQpgYGB7ciBwbG90bHlMTTJ9DQojIEUuZy4gaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9hLzM4NTMzMDQ2DQpwbG90U2FtcGxpbmdEYXRhTE0yUmVzIDwtIDMNCnBsb3RTYW1wbGluZ0RhdGFMTTJBeGlzWCA8LSBzb3J0KHVuaXF1ZShwbG90U2NhbGluZ0RhdGEkQmFzYWxzKSkNCiAgIyBzZXEobWluKHBsb3RTY2FsaW5nRGF0YSRCYXNhbHMpLCANCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXgocGxvdFNjYWxpbmdEYXRhJEJhc2FscyksDQogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBwbG90U2FtcGxpbmdEYXRhTE0yUmVzKSANCnBsb3RTYW1wbGluZ0RhdGFMTTJBeGlzWSA8LSBzb3J0KHVuaXF1ZShwbG90U2NhbGluZ0RhdGEkQ29uc3VtZXJzKSkNCiMgc2VxKG1pbihwbG90U2NhbGluZ0RhdGEkQ29uc3VtZXJzKSwgDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4KHBsb3RTY2FsaW5nRGF0YSRDb25zdW1lcnMpLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gcGxvdFNhbXBsaW5nRGF0YUxNMlJlcykgDQpwbG90U2FtcGxpbmdEYXRhTE0yU3VyZiA8LSBleHBhbmQuZ3JpZCgNCiAgQmFzYWxzID0gcGxvdFNhbXBsaW5nRGF0YUxNMkF4aXNYLA0KICBDb25zdW1lcnMgPSBwbG90U2FtcGxpbmdEYXRhTE0yQXhpc1kNCikgDQpwbG90U2FtcGxpbmdEYXRhTE0yU3VyZiRDb21tdW5pdHlTaXplIDwtIGV4cChwcmVkaWN0LmxtKA0KICBwbG90U2FtcGxpbmdEYXRhTE0yLCBuZXdkYXRhID0gcGxvdFNhbXBsaW5nRGF0YUxNMlN1cmYNCikpDQpwbG90U2FtcGxpbmdEYXRhTE0yU3VyZiA8LSByZXNoYXBlMjo6YWNhc3QoICMgQ2FzdCB0byBhcnJheS9tYXRyaXgNCiAgcGxvdFNhbXBsaW5nRGF0YUxNMlN1cmYsIEJhc2FscyB+IENvbnN1bWVycywgdmFsdWUudmFyID0gIkNvbW11bml0eVNpemUiDQopDQoNCnBsb3RTY2FsaW5nICU+JSBwbG90bHk6OmFkZF90cmFjZSh6ID0gcGxvdFNhbXBsaW5nRGF0YUxNMlN1cmYsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IHBsb3RTYW1wbGluZ0RhdGFMTTJBeGlzWCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gcGxvdFNhbXBsaW5nRGF0YUxNMkF4aXNZLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAic3VyZmFjZSIpDQpgYGANCg0KTm90IHBhcnRpY3VsYXJseSBjb252aW5jaW5nLCBidXQgaXQgZG9lcyBhZ3JlZSB3aXRoIGdlbmVyYWwgaWRlYXMgKGNvbnN1bWVycyBsZXNzIGltcG9ydGFudCB0aGFuIGJhc2FscykuDQoNCmBgYHtyIGxpbmVhcm1vZGVsM30NCnBsb3RTYW1wbGluZ0RhdGFMTTMgPC0gbG0oDQogIGxvZyhDb21tdW5pdHlTaXplKSB+IGxvZyhCYXNhbHMpICsgbG9nKENvbnN1bWVycykgLSAxLCANCiAgZGF0YSA9IHBsb3RTY2FsaW5nRGF0YQ0KKQ0Kc3VtbWFyeShwbG90U2FtcGxpbmdEYXRhTE0zKQ0KYGBgDQoNCmBgYHtyIHBsb3RseUxNM30NCiMgRS5nLiBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL2EvMzg1MzMwNDYNCnBsb3RTYW1wbGluZ0RhdGFMTTNSZXMgPC0gMw0KcGxvdFNhbXBsaW5nRGF0YUxNM0F4aXNYIDwtIHNvcnQodW5pcXVlKHBsb3RTY2FsaW5nRGF0YSRCYXNhbHMpKQ0KICAjIHNlcShtaW4ocGxvdFNjYWxpbmdEYXRhJEJhc2FscyksIA0KICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heChwbG90U2NhbGluZ0RhdGEkQmFzYWxzKSwNCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IHBsb3RTYW1wbGluZ0RhdGFMTTJSZXMpIA0KcGxvdFNhbXBsaW5nRGF0YUxNM0F4aXNZIDwtIHNvcnQodW5pcXVlKHBsb3RTY2FsaW5nRGF0YSRDb25zdW1lcnMpKQ0KIyBzZXEobWluKHBsb3RTY2FsaW5nRGF0YSRDb25zdW1lcnMpLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXgocGxvdFNjYWxpbmdEYXRhJENvbnN1bWVycyksDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBwbG90U2FtcGxpbmdEYXRhTE0yUmVzKSANCnBsb3RTYW1wbGluZ0RhdGFMTTNTdXJmIDwtIGV4cGFuZC5ncmlkKA0KICBCYXNhbHMgPSBwbG90U2FtcGxpbmdEYXRhTE0zQXhpc1gsDQogIENvbnN1bWVycyA9IHBsb3RTYW1wbGluZ0RhdGFMTTNBeGlzWQ0KKSANCnBsb3RTYW1wbGluZ0RhdGFMTTNTdXJmJENvbW11bml0eVNpemUgPC0gZXhwKHByZWRpY3QubG0oDQogIHBsb3RTYW1wbGluZ0RhdGFMTTMsIG5ld2RhdGEgPSBwbG90U2FtcGxpbmdEYXRhTE0zU3VyZg0KKSkNCnBsb3RTYW1wbGluZ0RhdGFMTTNTdXJmIDwtIHJlc2hhcGUyOjphY2FzdCggIyBDYXN0IHRvIGFycmF5L21hdHJpeA0KICBwbG90U2FtcGxpbmdEYXRhTE0zU3VyZiwgQmFzYWxzIH4gQ29uc3VtZXJzLCB2YWx1ZS52YXIgPSAiQ29tbXVuaXR5U2l6ZSINCikNCg0KcGxvdFNjYWxpbmcgJT4lIHBsb3RseTo6YWRkX3RyYWNlKHogPSBwbG90U2FtcGxpbmdEYXRhTE0zU3VyZiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gcGxvdFNhbXBsaW5nRGF0YUxNM0F4aXNYLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBwbG90U2FtcGxpbmdEYXRhTE0zQXhpc1ksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJzdXJmYWNlIikNCmBgYA0KDQojIEhvdyBkbyB0aGV5IGludGVyYWN0IHdpdGggZWFjaCBvdGhlciAob24gaXNsYW5kcyk/DQoNCmBgYHtyIGxvYWRQb29sc01hdHN9DQojID4gcnVuaWYoMSkgKiAxRTgNCiMgWzFdIDgyNTk4Njc5DQpzZXQuc2VlZCg4MjU5ODY3OSkNCg0KbWF0cyA8LSBsaXN0KCkNCnBvb2xzYWxsIDwtIGxpc3QoKSAjIG5hbWUgcG9vbHMgdXNlZCBpbiBzYXZlIGRhdGE7IGJlIGNhcmVmdWwhDQoNCmZvciAoaSBpbiAxOmxlbmd0aChkaXJWaWtpbmcpKSB7DQogIHRlbXAgPC0gbG9hZChmaWxlLnBhdGgoDQogICAgZGlyVmlraW5nW2ldLCANCiAgICBwYXN0ZTAoIkxhd01vcnRvbjE5OTYtTnVtZXJpY2FsUG9vbENvbW11bml0eVNjYWxpbmctUG9vbE1hdHMiLCANCiAgICAgICAgICAgaWYgKGkgPiAxKSBpIGVsc2UgIiIsIA0KICAgICAgICAgICAiLlJEUyIpDQogICkpDQogIG1hdHNbW2ldXSA8LSBldmFsKHBhcnNlKHRleHQgPSB0ZW1wWzFdKSkNCiAgcG9vbHNhbGxbW2ldXSA8LSBldmFsKHBhcnNlKHRleHQgPSB0ZW1wWzJdKSkNCn0NCnBvb2xzIDwtIHBvb2xzYWxsDQoNCm9sZENhbmRpZGF0ZURhdGEgPC0gbG9hZChmaWxlLnBhdGgoZ2V0d2QoKSwgImNhbmRpZGF0ZURhdGFTb0Zhci5SZGF0YSIpKQ0Kb2xkQ2FuZGlkYXRlRGF0YSA8LSBldmFsKHBhcnNlKHRleHQgPSBvbGRDYW5kaWRhdGVEYXRhKSkNCmBgYA0KDQpgYGB7ciBjb21wdXRlQ2FuZGlkYXRlc30NCmNhbmRpZGF0ZURhdGEgPC0gcGxvdFNjYWxpbmdEYXRhICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogIENvbWJuTnVtLCBEYXRhc2V0DQopICU+JSBkcGx5cjo6bXV0YXRlKA0KICBPdGhlclN0ZWFkeVN0YXRlcyA9IGRwbHlyOjpuKCkgLSAxDQopICU+JSBkcGx5cjo6ZmlsdGVyKA0KICBPdGhlclN0ZWFkeVN0YXRlcyA+IDANCikNCmNhbmRpZGF0ZURhdGEgJT4lIGRwbHlyOjpzZWxlY3QoLUNvbW11bml0eVNlcSkNCmBgYA0KDQpgYGB7ciBsb2FkQWJ1bmRhbmNlc30NCiMgRmlyc3QsIGNoZWNrIGlmIGl0IGlzIGluIHRoZSBwYXJhbUZyYW1lLg0KIyBTZWNvbmQsIGNoZWNrIGlmIGl0IGlzIGluIHRoZSBzYXZlZCBkYXRhIGZyb20gdGhlIHByZXZpb3VzLg0KIyBPdGhlcndpc2UsIGlnbm9yZSBpdCwgd2UnbGwgZmlndXJlIG91dCB3aGF0IGl0IGlzIGFuZCB3aHkgaXQgaXMgbWlzc2luZyBsYXRlci4NCg0KY2FuZGlkYXRlRGF0YSRDb21tdW5pdHlBYnVuZCA8LSAiIg0KDQpmb3IgKHIgaW4gMTpucm93KGNhbmRpZGF0ZURhdGEpKSB7DQogICMgSUQgMTo0IGFyZSB1c2VkIHRvIGlkZW50aWZ5IHBhcmFtRnJhbWUsIDUgdXNlZCB0byBpZGVudGlmeSBhYnVuZGFuY2UNCiAgSUQgPC0gY2FuZGlkYXRlRGF0YVtyLCAxOjZdDQogIHBhcmFtRnJhbWVSb3cgPC0gcGFyYW1GcmFtZSAlPiUgZHBseXI6OmZpbHRlcigNCiAgICBDb21ibk51bSA9PSBJRCRDb21ibk51bSwNCiAgICBCYXNhbHMgPT0gSUQkQmFzYWxzLA0KICAgIENvbnN1bWVycyA9PSBJRCRDb25zdW1lcnMsDQogICAgRGF0YXNldCA9PSBJRCREYXRhc2V0DQogICkNCiAgDQogIGlmIChpcy5saXN0KHBhcmFtRnJhbWVSb3ckRW5kU3RhdGVBYnVuZGFuY2VbWzFdXSkpIHsNCiAgICBlbnRyeSA8LSB3aGljaChJRCRDb21tdW5pdGllcyA9PSBuYW1lcyhwYXJhbUZyYW1lUm93JEVuZFN0YXRlQWJ1bmRhbmNlW1sxXV0pKQ0KICAgIGlmIChsZW5ndGgoZW50cnkpKSB7DQogICAgICBjYW5kaWRhdGVEYXRhJENvbW11bml0eUFidW5kW3JdIDwtIHBhcmFtRnJhbWVSb3ckRW5kU3RhdGVBYnVuZGFuY2VbWzFdXVtbZW50cnldXQ0KICAgICAgbmV4dCgpDQogICAgfQ0KICB9DQogIA0KICBpZiAoSUQkRGF0YXNldCA9PSAiMjAyMS0wNCIpIHsNCiAgICANCiAgICBvbGRDYW5kRGF0Um93IDwtIG9sZENhbmRpZGF0ZURhdGEgJT4lIGRwbHlyOjpmaWx0ZXIoDQogICAgICBDb21ibk51bSA9PSBJRCRDb21ibk51bSwNCiAgICAgIEJhc2FscyA9PSBJRCRCYXNhbHMsDQogICAgICBDb25zdW1lcnMgPT0gSUQkQ29uc3VtZXJzLA0KICAgICAgQ29tbXVuaXRpZXMgPT0gSUQkQ29tbXVuaXRpZXMNCiAgICApDQogICAgDQogICAgaWYgKG5yb3cob2xkQ2FuZERhdFJvdykgPiAwKSB7DQogICAgICBpZiAob2xkQ2FuZERhdFJvdyRDb21tdW5pdHlBYnVuZCAhPSAiIikgew0KICAgICAgICBjYW5kaWRhdGVEYXRhJENvbW11bml0eUFidW5kW3JdIDwtIG9sZENhbmREYXRSb3ckQ29tbXVuaXR5QWJ1bmQNCiAgICAgIH0NCiAgICB9DQogIH0NCn0NCmBgYA0KDQpgYGB7ciBmaWx0ZXJOb0FidW5kfQ0KY2FuZGlkYXRlRGF0YSA8LSBjYW5kaWRhdGVEYXRhICU+JSBkcGx5cjo6ZmlsdGVyKENvbW11bml0eUFidW5kICE9ICIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENvbW11bml0eUFidW5kICE9ICJGYWlsdXJlIikNCmBgYA0KDQpgYGB7ciBjb21wdXRlUHJvZHVjdGl2aXR5fQ0KY2FuZGlkYXRlRGF0YSRDb21tdW5pdHlQcm9kIDwtIE5BDQpmb3IgKHIgaW4gMTpucm93KGNhbmRpZGF0ZURhdGEpKSB7DQogIGNhbmRpZGF0ZURhdGEkQ29tbXVuaXR5UHJvZFtyXSA8LSB3aXRoKA0KICAgIGNhbmRpZGF0ZURhdGFbciwgXSwgDQogICAgUk1UUkNvZGUyOjpQcm9kdWN0aXZpdHkoDQogICAgICBQb29sID0gcG9vbHNbW0RhdGFzZXRJRF1dW1tDb21ibk51bV1dLCANCiAgICAgIEludGVyYWN0aW9uTWF0cml4ID0gbWF0c1tbRGF0YXNldElEXV1bW0NvbWJuTnVtXV0sIA0KICAgICAgQ29tbXVuaXR5ID0gQ29tbXVuaXRpZXMsIA0KICAgICAgUG9wdWxhdGlvbnMgPSBDb21tdW5pdHlBYnVuZA0KICAgICkNCiAgKQ0KfQ0KYGBgDQoNCmBgYHtyIGlzbGFuZEZVTn0NCmlzbGFuZEZVTiA8LSBmdW5jdGlvbihpLCBkYXQsIHBvb2wsIG1hdCwgZG1hdCkgew0KICB0ZW1wIDwtIGRhdFtpLCBdDQogIFJNVFJDb2RlMjo6SXNsYW5kRHluYW1pY3MoDQogICAgUG9vbCA9IHBvb2wsDQogICAgSW50ZXJhY3Rpb25NYXRyaXggPSBtYXQsDQogICAgQ29tbXVuaXRpZXMgPSBjKA0KICAgICAgbGlzdCh0ZW1wJENvbW11bml0aWVzWzFdKSwNCiAgICAgIHJlcCgiIiwgbnJvdyhkbWF0KSAtIDIpLA0KICAgICAgdGVtcCRDb21tdW5pdGllc1syXQ0KICAgICksDQogICAgUG9wdWxhdGlvbnMgPSBjKA0KICAgICAgbGlzdCh0ZW1wJENvbW11bml0eUFidW5kWzFdKSwNCiAgICAgIHJlcCgiIiwgbnJvdyhkbWF0KSAtIDIpLA0KICAgICAgbGlzdCh0ZW1wJENvbW11bml0eUFidW5kWzJdKQ0KICAgICksDQogICAgRGlzcGVyc2FsUG9vbCA9IDAuMDAwMSwNCiAgICBEaXNwZXJzYWxJc2xhbmQgPSBkbWF0LA0KICApDQp9DQpgYGANCg0KYGBge3IgaXNsYW5kT25lVHdvfQ0KIyBGb3IgZWFjaCBncm91cC1kYXRhc2V0LA0KIyBGb3IgZWFjaCBwYWlyLA0KIyBSdW4gSXNsYW5kIER5bmFtaWNzLA0KIyBTYXZlIHRoZSByZXN1bHQgd2l0aCBpdHMgcGFpcmluZw0KY2FuZGlkYXRlRGF0YSRUb3RhbElEIDwtIHBhc3RlKGNhbmRpZGF0ZURhdGEkQ29tYm5OdW0sIGNhbmRpZGF0ZURhdGEkRGF0YXNldElEKQ0KDQppc2xhbmRJbnRlcmFjdGlvbnNPbmVUd28gPC0gbGlzdCgpDQoNCmZvciAoZ3JwIGluIHVuaXF1ZShjYW5kaWRhdGVEYXRhJFRvdGFsSUQpKSB7DQogIGNhbmRpZGF0ZURhdGFTdWJzZXQgPC0gY2FuZGlkYXRlRGF0YSAlPiUgZHBseXI6OmZpbHRlcihUb3RhbElEID09IGdycCkNCiAgDQogIGlmIChucm93KGNhbmRpZGF0ZURhdGFTdWJzZXQpID09IDEpIG5leHQoKQ0KICANCiAgcGFpcmluZ1Jlc3VsdHMgPC0gY29tYm4oDQogICAgbnJvdyhjYW5kaWRhdGVEYXRhU3Vic2V0KSwgMiwgDQogICAgaXNsYW5kRlVOLA0KICAgIGRhdCA9IGNhbmRpZGF0ZURhdGFTdWJzZXQsIA0KICAgIHBvb2wgPSBwb29sc1tbDQogICAgICBjYW5kaWRhdGVEYXRhU3Vic2V0JERhdGFzZXRJRFsxXQ0KICAgICAgXV1bW2NhbmRpZGF0ZURhdGFTdWJzZXQkQ29tYm5OdW1bMV1dXSwNCiAgICBtYXQgPSBtYXRzW1sNCiAgICAgIGNhbmRpZGF0ZURhdGFTdWJzZXQkRGF0YXNldElEWzFdDQogICAgICBdXVtbY2FuZGlkYXRlRGF0YVN1YnNldCRDb21ibk51bVsxXV1dLA0KICAgIGRtYXQgPSBtYXRyaXgoYygwLCAxLCAxLCAwKSwgbnJvdyA9IDIsIG5jb2wgPSAyKSwNCiAgICBzaW1wbGlmeSA9IEZBTFNFDQogICkNCiAgDQogIHBhaXJpbmdSZXN1bHRzIDwtIGxhcHBseSgNCiAgICBwYWlyaW5nUmVzdWx0cywgZnVuY3Rpb24obWF0LCBpc2xlcykgew0KICAgICAgbWF0IDwtIG1hdFtucm93KG1hdCksIC0xXQ0KICAgICAgcmV0VmFsIDwtIGxpc3QoKQ0KICAgICAgc3BlY2llcyA8LSBsZW5ndGgobWF0KSAvIGlzbGVzDQogICAgICBmb3IgKGkgaW4gMTppc2xlcykgew0KICAgICAgICByZXRWYWxbW2ldXSA8LSBtYXRbKChpIC0gMSkgKiBzcGVjaWVzICsgMSkgOiAoaSAqIHNwZWNpZXMpXQ0KICAgICAgfQ0KICAgICAgcmV0VmFsDQogICAgfSwNCiAgICBpc2xlcyA9IDINCiAgKQ0KICANCiAgaXNsYW5kSW50ZXJhY3Rpb25zT25lVHdvW1tncnBdXSA8LSBwYWlyaW5nUmVzdWx0cw0KfQ0KYGBgDQoNCmBgYHtyIGlzbGFuZE9uZUVtcHR5VHdvfQ0KaXNsYW5kSW50ZXJhY3Rpb25zT25lRW1wdHlUd28gPC0gbGlzdCgpDQoNCmZvciAoZ3JwIGluIHVuaXF1ZShjYW5kaWRhdGVEYXRhJFRvdGFsSUQpKSB7DQogIGNhbmRpZGF0ZURhdGFTdWJzZXQgPC0gY2FuZGlkYXRlRGF0YSAlPiUgZHBseXI6OmZpbHRlcihUb3RhbElEID09IGdycCkNCiAgDQogIGlmIChucm93KGNhbmRpZGF0ZURhdGFTdWJzZXQpID09IDEpIG5leHQoKQ0KICANCiAgcGFpcmluZ1Jlc3VsdHMgPC0gY29tYm4oDQogICAgbnJvdyhjYW5kaWRhdGVEYXRhU3Vic2V0KSwgMiwgDQogICAgaXNsYW5kRlVOLA0KICAgIGRhdCA9IGNhbmRpZGF0ZURhdGFTdWJzZXQsIA0KICAgIHBvb2wgPSBwb29sc1tbDQogICAgICBjYW5kaWRhdGVEYXRhU3Vic2V0JERhdGFzZXRJRFsxXQ0KICAgICAgXV1bW2NhbmRpZGF0ZURhdGFTdWJzZXQkQ29tYm5OdW1bMV1dXSwNCiAgICBtYXQgPSBtYXRzW1sNCiAgICAgIGNhbmRpZGF0ZURhdGFTdWJzZXQkRGF0YXNldElEWzFdDQogICAgICBdXVtbY2FuZGlkYXRlRGF0YVN1YnNldCRDb21ibk51bVsxXV1dLA0KICAgIGRtYXQgPSBtYXRyaXgoYygNCiAgICAgIDAsIDEsIDAsICMgSXNsYW5kIDIgLT4gMQ0KICAgICAgMSwgMCwgMSwgIyBJc2xhbmQgMSAtPiAyLCBJc2xhbmQgMyAtPiAyDQogICAgICAwLCAxLCAwICAjIElzbGFuZCAyIC0+IDMNCiAgICApLCBucm93ID0gMywgbmNvbCA9IDMsIGJ5cm93ID0gVFJVRSksDQogICAgc2ltcGxpZnkgPSBGQUxTRQ0KICApDQogIA0KICBwYWlyaW5nUmVzdWx0cyA8LSBsYXBwbHkoDQogICAgcGFpcmluZ1Jlc3VsdHMsIGZ1bmN0aW9uKG1hdCwgaXNsZXMpIHsNCiAgICAgIG1hdCA8LSBtYXRbbnJvdyhtYXQpLCAtMV0NCiAgICAgIHJldFZhbCA8LSBsaXN0KCkNCiAgICAgIHNwZWNpZXMgPC0gbGVuZ3RoKG1hdCkgLyBpc2xlcw0KICAgICAgZm9yIChpIGluIDE6aXNsZXMpIHsNCiAgICAgICAgcmV0VmFsW1tpXV0gPC0gbWF0WygoaSAtIDEpICogc3BlY2llcyArIDEpIDogKGkgKiBzcGVjaWVzKV0NCiAgICAgIH0NCiAgICAgIHJldFZhbA0KICAgIH0sDQogICAgaXNsZXMgPSAzDQogICkNCiAgDQogIGlzbGFuZEludGVyYWN0aW9uc09uZUVtcHR5VHdvW1tncnBdXSA8LSBwYWlyaW5nUmVzdWx0cw0KfQ0KYGBgDQoNCmBgYHtyIGNvbXBhcmVJc2xhbmREeW5hbWljc30NCiMgRm9ybWF0IG9mIHRhYmxlIHNob3VsZCBiZToNCiMgSUQsIENvbW11bml0eSAxLCBDb21tdW5pdHkgMiwgT3V0Y29tZXMgMS0yLCBPdXRjb21lcyAxLTAtMg0KIyBGb3Igb3V0Y29tZXMsIHNwZWNpZXMgcHJlc2VuY2Ugd2lsbCBiZSB1c2VkLg0KDQpjb21tdW5pdGllcyA8LSBOVUxMDQp0b3RhbENvbW11bml0aWVzIDwtIE5VTEwNCmZvciAoZ3JwIGluIHVuaXF1ZShjYW5kaWRhdGVEYXRhJFRvdGFsSUQpKSB7DQogIGNhbmRpZGF0ZURhdGFTdWJzZXQgPC0gY2FuZGlkYXRlRGF0YSAlPiUgZHBseXI6OmZpbHRlcihUb3RhbElEID09IGdycCkNCiAgDQogIGlmIChucm93KGNhbmRpZGF0ZURhdGFTdWJzZXQpID4gMSkgew0KICAgIG5ld0NvbW11bml0aWVzIDwtIGNvbWJuKA0KICAgICAgY2FuZGlkYXRlRGF0YVN1YnNldCRDb21tdW5pdGllcywgMiwgDQogICAgKQ0KICAgIGNvbW11bml0aWVzIDwtIGMoY29tbXVuaXRpZXMsIG5ld0NvbW11bml0aWVzKQ0KICAgIHRvdGFsQ29tbXVuaXRpZXMgPC0gYygNCiAgICAgIHRvdGFsQ29tbXVuaXRpZXMsDQogICAgICB0b1N0cmluZyhzb3J0KHVuaXF1ZSh1bmxpc3QobGFwcGx5KG5ld0NvbW11bml0aWVzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUk1UUkNvZGUyOjpDc3ZSb3dTcGxpdCkpKSkpDQogICAgKQ0KICB9DQp9DQoNCmlzbGFuZEludGVyYWN0aW9uc09uZVR3b1doaWNoIDwtIHVubGlzdChsYXBwbHkoDQogIHNlcV9hbG9uZyhpc2xhbmRJbnRlcmFjdGlvbnNPbmVUd28pLCBmdW5jdGlvbihpLCB4LCB0Qykgew0KICAgIGxhcHBseSh4W1tpXV0sIGZ1bmN0aW9uKHksIHRDKSB7DQogICAgICBsYXBwbHkoeSwgZnVuY3Rpb24oeiwgdEMpIHsNCiAgICAgICAgdG9TdHJpbmcoUk1UUkNvZGUyOjpDc3ZSb3dTcGxpdCh0Qylbd2hpY2goeiA+IDFFLTYpXSkNCiAgICAgIH0sIHRDID0gdEMpDQogICAgICB9LA0KICAgICAgdEMgPSB0Q1tpXSkNCiAgfSwNCiAgeCA9IGlzbGFuZEludGVyYWN0aW9uc09uZVR3bywNCiAgdEMgPSB0b3RhbENvbW11bml0aWVzKSkNCg0KaXNsYW5kSW50ZXJhY3Rpb25zT25lRW1wdHlUd29XaGljaCA8LSB1bmxpc3QobGFwcGx5KA0KICBzZXFfYWxvbmcoaXNsYW5kSW50ZXJhY3Rpb25zT25lRW1wdHlUd28pLCBmdW5jdGlvbihpLCB4LCB0Qykgew0KICAgIGxhcHBseSh4W1tpXV0sIGZ1bmN0aW9uKHksIHRDKSB7DQogICAgICBsYXBwbHkoeSwgZnVuY3Rpb24oeiwgdEMpIHsNCiAgICAgICAgdG9TdHJpbmcoUk1UUkNvZGUyOjpDc3ZSb3dTcGxpdCh0Qylbd2hpY2goeiA+IDFFLTYpXSkNCiAgICAgIH0sIHRDID0gdEMpDQogICAgICB9LA0KICAgICAgdEMgPSB0Q1tpXSkNCiAgfSwNCiAgeCA9IGlzbGFuZEludGVyYWN0aW9uc09uZUVtcHR5VHdvLA0KICB0QyA9IHRvdGFsQ29tbXVuaXRpZXMpKQ0KDQoNCmlzbGFuZEludGVyYWN0aW9uUmVzdWx0cyA8LSBkYXRhLmZyYW1lKA0KICBEYXRhc2V0SUQgPSByZXAobmFtZXMoaXNsYW5kSW50ZXJhY3Rpb25zT25lVHdvKSwgDQogICAgICAgICAgICAgICAgICB1bmxpc3QobGFwcGx5KGlzbGFuZEludGVyYWN0aW9uc09uZVR3bywgbGVuZ3RoKSkpLA0KICBDb21tdW5pdHkxID0gY29tbXVuaXRpZXNbc2VxKGZyb20gPSAxLCB0byA9IGxlbmd0aChjb21tdW5pdGllcyksIGJ5ID0gMildLA0KICBDb21tdW5pdHkyID0gY29tbXVuaXRpZXNbc2VxKGZyb20gPSAyLCB0byA9IGxlbmd0aChjb21tdW5pdGllcyksIGJ5ID0gMildLA0KICBPdXRjb21lV09FbXB0eV9Jc2xhbmQxID0gaXNsYW5kSW50ZXJhY3Rpb25zT25lVHdvV2hpY2hbDQogICAgc2VxKGZyb20gPSAxLCB0byA9IGxlbmd0aChpc2xhbmRJbnRlcmFjdGlvbnNPbmVUd29XaGljaCksIGJ5ID0gMildLA0KICBPdXRjb21lV09FbXB0eV9Jc2xhbmQyID0gaXNsYW5kSW50ZXJhY3Rpb25zT25lVHdvV2hpY2hbDQogICAgc2VxKGZyb20gPSAxLCB0byA9IGxlbmd0aChpc2xhbmRJbnRlcmFjdGlvbnNPbmVUd29XaGljaCksIGJ5ID0gMildLA0KICBPdXRjb21lV0VtcHR5X0lzbGFuZDEgPSBpc2xhbmRJbnRlcmFjdGlvbnNPbmVFbXB0eVR3b1doaWNoWw0KICAgIHNlcShmcm9tID0gMSwgdG8gPSBsZW5ndGgoaXNsYW5kSW50ZXJhY3Rpb25zT25lRW1wdHlUd29XaGljaCksIGJ5ID0gMyldLA0KICBPdXRjb21lV0VtcHR5X0lzbGFuZDIgPSBpc2xhbmRJbnRlcmFjdGlvbnNPbmVFbXB0eVR3b1doaWNoWw0KICAgIHNlcShmcm9tID0gMSwgdG8gPSBsZW5ndGgoaXNsYW5kSW50ZXJhY3Rpb25zT25lRW1wdHlUd29XaGljaCksIGJ5ID0gMyldLA0KICBPdXRjb21lV0VtcHR5X0lzbGFuZDMgPSBpc2xhbmRJbnRlcmFjdGlvbnNPbmVFbXB0eVR3b1doaWNoWw0KICAgIHNlcShmcm9tID0gMSwgdG8gPSBsZW5ndGgoaXNsYW5kSW50ZXJhY3Rpb25zT25lRW1wdHlUd29XaGljaCksIGJ5ID0gMyldDQopDQoNCmlzbGFuZEludGVyYWN0aW9uUmVzdWx0cw0KYGBgDQoNCmBgYHtyIG1hdGNoZXN9DQppc2xhbmRJbnRlcmFjdGlvblJlc3VsdHMgJT4lIGRwbHlyOjptdXRhdGUoDQogIEMxV09JbnZhZGVkID0gQ29tbXVuaXR5MSAhPSBPdXRjb21lV09FbXB0eV9Jc2xhbmQxLA0KICBDMldPSW52YWRlZCA9IENvbW11bml0eTIgIT0gT3V0Y29tZVdPRW1wdHlfSXNsYW5kMiwNCiAgQzFXSW52YWRlZCA9IENvbW11bml0eTEgIT0gT3V0Y29tZVdFbXB0eV9Jc2xhbmQxLA0KICBDMldJbnZhZGVkID0gQ29tbXVuaXR5MiAhPSBPdXRjb21lV0VtcHR5X0lzbGFuZDMsDQogIFN0YWxlbWF0ZVdPID0gIUMxV09JbnZhZGVkICYgIUMyV09JbnZhZGVkLA0KICBTdGFsZW1hdGVXID0gIUMxV0ludmFkZWQgJiAhQzJXSW52YWRlZCwNCiAgSHlicmlkV08gPSBDMVdPSW52YWRlZCAmIEMyV09JbnZhZGVkLA0KICBIeWJyaWRXID0gQzFXSW52YWRlZCAmIEMyV0ludmFkZWQsDQopICU+JSBkcGx5cjo6c2VsZWN0KC1kcGx5cjo6c3RhcnRzX3dpdGgoIk91dGNvbWUiKSkNCmBgYA0K